#include "ConfigurationManager.h"
#include "ConfigurationFilePart.h"
#include "StoneEntity.h"
#include "GhostEntity.h"
#include "PacmanEntity.h"
#include "AppleEntity.h"
#include <fstream>
#include <iostream>

ConfigurationManager::ConfigurationManager(void) {}

ConfigurationManager::~ConfigurationManager(void) {}

void ConfigurationManager::Trim(std::string& pLine)
{
	// Remove leading and trailing whitespace
	static const char vWhitespace[] = " \n\t\v\r\f";
	pLine.erase( 0, pLine.find_first_not_of(vWhitespace) );
	pLine.erase( pLine.find_last_not_of(vWhitespace) + 1U );
}

int ConfigurationManager::IsIgnorable(std::string& pLine)
{
	return (pLine.length() == 0) || (pLine[0] == '#');
}

void ConfigurationManager::CreateGhostsInLevel(Level* pLevel, int pNbGhost)
{
	//TODO
}

int ConfigurationManager::ExtractLevelElementInLine(std::string& pLine, std::string& pLevelElement)
{
	//Check whether the line contains level element
	if(pLine.length() == 0) return false;

	//Look for different delimiter: space and tab
	int vFirstSpacePosition = pLine.find(' ');
	int vFirstTabPosition = pLine.find('\t');

	//Look for first delimiter: space ,tab or end of line
	int vDelimiter = vFirstSpacePosition > vFirstTabPosition ? vFirstTabPosition : vFirstSpacePosition;
	if(vDelimiter <=0)
		vDelimiter = vFirstSpacePosition < vFirstTabPosition ? vFirstTabPosition : vFirstSpacePosition;
	if(vDelimiter <= 0)
		vDelimiter = pLine.length();

	//Extract level element
	pLevelElement = pLine.substr(0, vDelimiter);
	pLine = pLine.substr(vDelimiter, pLine.length());

	//Trim
	Trim(pLevelElement);
	Trim(pLine);

	return true;
}

void ConfigurationManager::SetPropertyInLevel(std::string& pLine, Level* pLevel)
{
	int vEqualPosition = pLine.find('=');
	std::string vKey = pLine.substr(0, vEqualPosition);
	std::string vValue = pLine.substr(vEqualPosition + 1, pLine.length() - 1);
	Trim(vKey);
	Trim(vValue);

	if(strcmp(vKey.c_str(), "GhostNb") == 0)
	{
		int pNbGhost = atoi(vValue.c_str());
		CreateGhostsInLevel(pLevel, pNbGhost);
		std::cout << pNbGhost << " ghosts created in level" << std::endl;
	}
	else
		std::cerr << "Unknown configuration key property: " << vKey << std::endl;
}

void ConfigurationManager::PushObjectsInLevel(std::string& pLine, Level* pLevel, int pY)
{
	int vX = 0;
	Trim(pLine);
	std::string vLevelElement;
	while(ExtractLevelElementInLine(pLine, vLevelElement))
	{
		//Check Stone1
		if(strcmp(vLevelElement.c_str(), Conf_Stone1) == 0)
			pLevel->PushLevelElementInGrid( new StoneEntity(vX, pY) );
			//pLevel->GetListLevelElement().push_back( new StoneEntity(vX, pY) );

		//Check Apple
		else if(strcmp(vLevelElement.c_str(), Conf_Apple) == 0)
			//pLevel->GetListLevelElement().push_back( new AppleEntity(vX, pY) );
			pLevel->PushLevelElementInGrid( new AppleEntity(vX, pY) );

		//Check void
		else if(strcmp(vLevelElement.c_str(), Conf_Nothing) == 0){}

		//Cheack ghost generator
		else if(strcmp(vLevelElement.c_str(), Conf_GhostGene) == 0)
			//TODO!
			std::cout<< "Ghost generator is not implemented yet!" << std::endl;

		//Check Pacman
		else if(strcmp(vLevelElement.c_str(), Conf_Pacman) == 0)
			pLevel->GetListActor().push_back( new PacmanEntity(vX, pY) );

		else
			std::cout << "No matching element found for \"" << vLevelElement << "\"" << std::endl;

		vX += Conf_LevelElementSize;
	}
}

Level* ConfigurationManager::CreateLevel(const std::string &Filename)
{
	//Try to open the level file
	std::ifstream vIfstream(Filename.c_str(), std::ios::_Nocreate);
	if(vIfstream == NULL)
	{
		std::cerr << "Error while opening config file"<<std::endl;
		exit(EXIT_FAILURE);
	}

	//Convert the content of the file into level instance
	int vYLevelPosition = 0;
	Level *vLevel = new Level();
	char vCurrentLine[800];
	ConfigurationFilePart::ConfigurationFilePartEnum vPart = ConfigurationFilePart::None;
	int vPartIsEnd = true;
	while(vIfstream.peek() != EOF)
	{
		//Get next line
		vIfstream.getline(vCurrentLine, 800);
		Trim((std::string)vCurrentLine);

		//Check whether the current line is ignorable
		if(!IsIgnorable((std::string)vCurrentLine))
		{
			switch(vPart)
			{

			//None
			case ConfigurationFilePart::None:
				if(strcmp(vCurrentLine, ConfPart_Property) != 0)
				{
					std::cerr<<"Ignoring not expected configuration line \""<<vCurrentLine<<"\"!"<<std::endl;
					break;
				}
				//Change part mode -> begining of the Property part
				vPart = ConfigurationFilePart::Property;
				vPartIsEnd = false;
				break;
			
			//[Property Begin]
			case ConfigurationFilePart::Property:
				if(!vPartIsEnd)
				{
					if(strcmp(vCurrentLine, ConfPart_End) == 0)
					{
						//End of the Property part
						vPartIsEnd = true;
						break;
					}
					//Process the current line
					SetPropertyInLevel((std::string)vCurrentLine, vLevel);
				}
				else
				{
					if(strcmp(vCurrentLine, ConfPart_Level) != 0)
					{
						std::cerr<<"Ignoring not expected configuration line \""<<vCurrentLine<<"\"!"<<std::endl;
						break;
					}
					//Change part mode -> begining of the Level part
					vPart = ConfigurationFilePart::LevelGrid;
					vPartIsEnd = false;
				}
				break;

			//[Level Begin]
			case ConfigurationFilePart::LevelGrid:
				if(!vPartIsEnd)
				{
					if(strcmp(vCurrentLine, ConfPart_End) == 0)
					{
						//End of the level part
						vPartIsEnd = true;
						break;
					}
					//Process the current line
					PushObjectsInLevel((std::string)vCurrentLine, vLevel, vYLevelPosition);
					vYLevelPosition += Conf_LevelElementSize;
				}
				else
					std::cerr
					<<"All peaces of information retrieved from file. Unused \""
					<<vCurrentLine<<"\"!"<<std::endl;
				break;
			}
		}
	}

	//Close the file stream
	vIfstream.close();

	//Check loading process
	if( vPart == ConfigurationFilePart::LevelGrid && vPartIsEnd)
	{
		std::cout << "Level loaded successfully!" << std::endl;
	}
	else
	{
		std::cerr << "The level has not been loaded properly. Application exiting now!" <<std::endl;
		exit(EXIT_FAILURE);
	}

	return vLevel;
}
